package com.merry.nio;

import org.junit.Test;

import java.nio.ByteBuffer;

/**
 *使用Buffer读写数据一般遵循以下四个步骤：
 * 1 写入数据到Buffer
 * 2 调用flip()方法，转换为读取模式
 * 3 从Buffer中读取数据
 * 4 调用buffer.clear()方法或者buffer.compact()方法清除缓冲区
 *  buffer的很多方法都是重新赋值 limit，position，mark的过程。而buffer也就是通过设置这几个参数的值来控制这个buffer是写入，还是读取的。
 *
 */
public class TestBuffer {


    /**
     * 我们在创建一个buffer对象的时候，是可以指定直接内存和非直接内存的。
     * 什么是直接内存与非直接内存：
     * 根据官方文档的描述：
     * byte byffer可以是两种类型，一种是基于直接内存（也就是
     * 非堆内存）；另一种是非直接内存（也就是堆内存）。对于直
     * 接内存来说，JVM将会在IO操作上具有更高的性能，因为它
     * 直接作用于本地系统的IO操作。而非直接内存，也就是堆内
     * 存中的数据，如果要作IO操作，会先从本进程内存复制到直接
     * 内存，再利用本地IO处理。
     *
     * 从数据流的角度，非直接内存是下面这样的作用链：
     * 本地IO-->直接内存-->非直接内存-->直接内存-->本地IO
     *
     * 而直接内存是：
     * 本地IO-->直接内存-->本地IO
     *
     * 很明显，在做IO处理时，比如网络发送大量数据时，直接内
     * 存会具有更高的效率。直接内存使用allocateDirect创建，但
     * 是它比申请普通的堆内存需要耗费更高的性能。不过，这
     * 部分的数据是在JVM之外的，因此它不会占用应用的内
     * 存。所以呢，当你有很大的数据要缓存，并且它的生命
     * 周期又很长，那么就比较适合使用直接内存。只是一般
     * 来说，如果不是能带来很明显的性能提升，还是推荐直接
     * 使用堆内存。字节缓冲区是直接缓冲区还是非直接缓冲
     * 区可通过调用其 isDirect()  方法来确定。
     *
     * 使用场景
     *
     * 1 有很大的数据需要存储，它的生命周期又很长
     * 2 适合频繁的IO操作，比如网络并发场景
     */
    @Test
    public void test3(){
        //分配直接缓冲区
        ByteBuffer buf = ByteBuffer.allocateDirect(1024);
        //分类非直接内存。
        ByteBuffer buf1 = ByteBuffer.allocate(1024);
        System.out.println(buf1.isDirect());
        System.out.println(buf.isDirect());
    }

    /**
     * 使用buffer的案例
     */
    @Test
    public void test2(){
        String str = "itheima";
        //创建一个容量为1024的buffer
        ByteBuffer buf = ByteBuffer.allocate(1024);
        //1.将数据写入buffer。
        buf.put(str.getBytes());
        //2.将buffer切换为读取模式。
        buf.flip();
        //3.读取buffer中的数据。
        byte[] dst = new byte[buf.limit()];
        buf.get(dst, 0, 2);
        System.out.println(new String(dst, 0, 2));
        System.out.println(buf.position());

        //mark() : 标记
        buf.mark();

        buf.get(dst, 2, 2);
        System.out.println(new String(dst, 2, 2));
        System.out.println(buf.position());

        //reset() : 恢复到 mark 的位置
        buf.reset();
        System.out.println(buf.position());

        //判断缓冲区中是否还有剩余数据
        if(buf.hasRemaining()){
            //获取缓冲区中可以操作的数量
            System.out.println(buf.remaining());
        }
    }

    /**
     * 1.新建一个buffer。
     * 2.写入数据到Buffer
     * 3.调用flip()方法，转换为读取模式
     * 4.从Buffer中读取数据
     * 5.调用buffer.clear()方法或者buffer.compact()方法清除缓冲区，清除缓冲区，相当于切换到写入模式了。
     *
     */
    @Test
    public void test1(){
        String str = "itheima";
        //1. 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        System.out.println("-----------------allocate()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //2. 利用 put() 存入数据到缓冲区中
        buf.put(str.getBytes());
        System.out.println("-----------------put()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //3. 切换读取数据模式
        buf.flip();
        System.out.println("-----------------flip()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //4. 利用 get() 读取缓冲区中的数据
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst, 0, dst.length));

        System.out.println("-----------------get()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //5. rewind() : 可重复读
        buf.rewind();
        System.out.println("-----------------rewind()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在，但是处于“被遗忘”状态
        buf.clear();
        System.out.println("-----------------clear()----------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        System.out.println((char)buf.get());
    }

}

